package utils

import (
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
	"path"
	"strings"
	"time"
)

const (
	KB              int64 = 1 << 10
	MB              int64 = 1 << 20
	UploadSizeLimit int64 = 100
)

type UploadLib struct {
	config *UploadConfig
}

func NewUploadLib(config *UploadConfig) *UploadLib {
	return &UploadLib{config: config}
}

type UploadConfig struct {
	Key       string
	Req       *http.Request
	AllowExts []string
	SaveDir   string
	LimitSize int64
	Prefix    string
}

func NewUploadConfig(req *http.Request, key, saveDir, prefix string, allowExts []string, limitSize int64) *UploadConfig {
	return &UploadConfig{Req: req, Key: key, SaveDir: saveDir, Prefix: prefix, AllowExts: allowExts, LimitSize: limitSize}
}

type UploadFileParam struct {
	OriginName string // 上传文件名
	Name       string // 保存后的文件名
}

/**
 * 基础检查
 */
func (m *UploadLib) baseCheck() ([]*multipart.FileHeader, error) {
	if m.config.Req.Method != "POST" {
		Printf("http method not post")
		return nil, fmt.Errorf("HTTP METHOD IS NOT POST")
	}
	if m.config.Req.ContentLength <= 0 {
		Printf("http content is null")
		return nil, fmt.Errorf("request Content IS EMPTY")
	}
	err := m.config.Req.ParseMultipartForm(UploadSizeLimit * MB)
	if err != nil {
		Printf("ParseMultipartForm error:%s", err.Error())
		return nil, err
	}
	if m.config.Req.MultipartForm == nil || m.config.Req.MultipartForm.File == nil {
		Printf("MultipartForm empty error:%s", err.Error())
		return nil, http.ErrMissingFile
	}
	files, ok := m.config.Req.MultipartForm.File[m.config.Key]
	if !ok || len(files) == 0 {
		return nil, http.ErrMissingFile
	}
	return files, nil
}

/**
 * 文件检查
 */
func (m *UploadLib) fileCheck(files []*multipart.FileHeader) error {
	// 文件检查
	for _, file := range files {
		if m.config.LimitSize > 0 && file.Size > m.config.LimitSize {
			Printf("file size over limit,filesize:%d,filename:%s,config:%+v", file.Size, file.Filename, m.config)
			return fmt.Errorf("file size over limit")
		}
		ext := strings.TrimLeft(path.Ext(file.Filename), ".")
		if len(m.config.AllowExts) != 0 {
			if !InArrayString(ext, m.config.AllowExts) {
				return fmt.Errorf("not allow file type extension only filename:%s ,allowExt:%+v", file.Filename, m.config.AllowExts)
			}
		}
	}
	return nil
}

func (m *UploadLib) Check() error {
	_, err := m.doCheck()
	return err
}

func (m *UploadLib) getFileName(filename string) string {
	ext := path.Ext(filename)
	if m.config.Prefix != "" {
		return fmt.Sprintf("%s%s-%d%s", m.config.Prefix, filename, time.Now().Unix(), ext)
	}
	return fmt.Sprintf("%s-%d%s", filename, time.Now().Unix(), ext)
}

/**
 * 检查
 */
func (m *UploadLib) doCheck() ([]*multipart.FileHeader, error) {
	files, err := m.baseCheck()
	if err != nil {
		Printf("base check error:%s,config:%+v", err.Error(), m.config)
		return nil, err
	}
	err = m.fileCheck(files)
	if err != nil {
		Printf("file check error:%s,config:%+v", err.Error(), m.config)
		return nil, err
	}
	if m.config.SaveDir != "" {
		err = m.mkdir(m.config.SaveDir)
		if err != nil {
			Printf("mkdir error:%s,config:%+v", err.Error(), m.config)
			return nil, err
		}
	}
	return files, nil
}

// /**
//  * 多文件上传
//  */
// func (m *UploadLib) MultipleUpload() ([]UploadFileParam, error) {
// 	if m.config.SaveDir == "" {
// 		return nil, fmt.Errorf("save path is empty")
// 	}
// 	files, err := m.doCheck()
// 	if err != nil {
// 		return nil, err
// 	}
// 	succ := []UploadFileParam{}
// 	for k, v := range files {
// 		tmp, err := m.doUpload(k+1, v)
// 		if err != nil {
// 			fmt.Printf("write file info:%+v error:%s", tmp, err.Error())
// 			m.DeleteFile(succ)
// 			return nil, err
// 		}
// 		succ = append(succ, tmp)
// 	}
// 	return succ, nil
// }

func (m *UploadLib) doUpload(file *multipart.FileHeader) (UploadFileParam, error) {
	fileName := m.getFileName(file.Filename)
	tmp := UploadFileParam{}
	tmp.OriginName = file.Filename
	tmp.Name = fileName
	err := m.writeFile(file, fileName)
	if err != nil {
		Printf("write file info:%+v error:%s", tmp, err.Error())
	}
	return tmp, err
}

/**
 * 单文件上传
 */
func (m *UploadLib) Upload() (UploadFileParam, error) {
	rs := UploadFileParam{}
	if m.config.SaveDir == "" {
		return rs, fmt.Errorf("save path is empty")
	}
	files, err := m.doCheck()
	if err != nil {
		return rs, err
	}
	rs, err = m.doUpload(files[0])
	if err != nil {
		Printf("do upload error:%s", err.Error())
	}
	// fmt.Printf("do upload success,rs:%+v\n", rs)
	return rs, err
}

/**
 * 获取上传文件内容
 */
func (m *UploadLib) GetUploadContent() ([]byte, string, error) {
	files, err := m.doCheck()
	if err != nil {
		Printf("upload check error:%s", err.Error())
		return nil, "", err
	}
	file, err := files[0].Open()
	if err != nil {
		Printf("open upload file error:%s", err.Error())
		return nil, "", err
	}
	defer file.Close()
	rs, err := ioutil.ReadAll(file)
	if err != nil {
		Printf("upload get file content  readall errorr:%s", err.Error())
	}
	return rs, files[0].Filename, err
}

/**
 * 删除文件
 */
func (m *UploadLib) DeleteFile(params ...UploadFileParam) {
	for _, v := range params {
		filepath := m.config.SaveDir + "/" + v.Name
		err := os.Remove(filepath)
		if err != nil {
			Printf("delete file:%s error:%s", filepath, err.Error())
		}
	}
}

/**
 * 获取文件保存路径
 */
func (m *UploadLib) GetFilePath(params UploadFileParam) string {
	return m.config.SaveDir + "/" + params.Name
}

/**
 * 文件写入
 */
func (m *UploadLib) writeFile(file *multipart.FileHeader, fileName string) error {
	filepath := m.config.SaveDir + "/" + fileName
	targetFile, err := os.Create(filepath)
	if err != nil {
		Printf("create filepath:%s,error:%s", filepath, err.Error())
		return err
	}
	defer targetFile.Close()
	fileHandle, err := file.Open()
	if err != nil {
		Printf("file open error:%s", err.Error())
		return err
	}
	defer fileHandle.Close()
	_, err = io.Copy(targetFile, fileHandle)
	if err != nil {
		Printf("io copy error:%s", err.Error())
		return err
	}
	return nil
}

/**
 * 创建文件夹
 */
func (m *UploadLib) mkdir(dirPath string) error {
	if !PathExists(dirPath) {
		return os.MkdirAll(dirPath, os.ModePerm)
	}
	return nil
}
